package day24.map;

import java.util.*;

/**
 * Map接口功能测试类
 * 演示Map接口的基本操作，包括添加、删除、查询、遍历等
 * Map是键值对的集合，每个键最多只能映射到一个值
 * 常见实现类包括HashMap、LinkedHashMap、TreeMap等
 * 
 * HashMap特点：
 * 1. 基于哈希表实现，提供快速的查找、插入和删除操作
 * 2. 无序存储，不保证元素的顺序
 * 3. 允许一个null键和多个null值
 * 4. 线程不安全，多线程环境下需要外部同步
 * 5. 默认初始容量为16，负载因子为0.75
 * 
 * LinkedHashMap特点：
 * 1. 继承自HashMap，具有HashMap的所有特性
 * 2. 通过维护双向链表保持元素的插入顺序
 * 3. 可以按访问顺序或插入顺序迭代元素
 * 4. 性能略低于HashMap，因为需要维护链表结构
 * 
 * TreeMap特点：
 * 1. 基于红黑树实现，能够按键的自然顺序或自定义比较器排序
 * 2. 提供有序的键值对存储
 * 3. 不允许键为null（会抛出NullPointerException）
 * 4. 基本操作（添加、删除、查找）的时间复杂度为O(log n)
 * 5. 比HashMap和LinkedHashMap占用更多内存空间
 */
public class MapTest {
    public static void main(String[] args) {
        // 1. HashMap测试 - 最常用的Map实现，基于哈希表，无序，允许null键和null值
        // HashMap提供了快速的查找、插入和删除操作，时间复杂度平均为O(1)
        System.out.println("======= HashMap测试 =======");
        // 创建一个HashMap实例，键为String类型，值为String类型
        Map<String, String> hashMap = new HashMap<>();
        
        // 添加键值对 - put方法将指定的键值对添加到Map中
        // 如果Map中已存在相同的键，则旧值会被新值替换
        hashMap.put("name", "张三");     // 添加姓名键值对
        hashMap.put("age", "25");       // 添加年龄键值对
        hashMap.put("city", "北京");     // 添加城市键值对
        hashMap.put("job", "程序员");    // 添加职业键值对
        hashMap.put(null, "空键值");     // HashMap允许null键,null键只能有一个
        hashMap.put("nullValue", null); // HashMap允许null值,null值可以重复
        
        // 打印整个HashMap的内容，格式为{key1=value1, key2=value2, ...}
        System.out.println("HashMap内容: " + hashMap);
        // 获取HashMap中键值对的数量
        System.out.println("HashMap大小: " + hashMap.size());
        
        // 根据键获取对应的值 - get方法返回指定键所映射的值，如果不存在该键则返回null
        System.out.println("姓名: " + hashMap.get("name"));
        System.out.println("测试不存在的键：" + hashMap.get("测试不存在的键"));
        // 获取null键对应的值
        System.out.println("空键对应的值: " + hashMap.get(null));
        
        // 检查Map中是否包含指定的键 - containsKey方法检查Map是否包含指定的键
        System.out.println("是否包含键'age': " + hashMap.containsKey("age"));
        // 检查Map中是否包含指定的值 - containsValue方法检查Map是否包含指定的值
        System.out.println("是否包含值'程序员': " + hashMap.containsValue("程序员"));
        
        // 删除指定键的键值对 - remove方法移除指定键的映射关系，并返回被移除的值
        String removedValue = hashMap.remove("job");
        System.out.println("删除的job值: " + removedValue);
        // 打印删除指定键值对后的HashMap
        System.out.println("删除后的HashMap: " + hashMap);
        
        // 2. LinkedHashMap测试 - 保持插入顺序的Map实现
        // LinkedHashMap继承自HashMap，通过维护一个双向链表来保持元素的插入顺序
        System.out.println("\n======= LinkedHashMap测试 =======");
        // 创建LinkedHashMap实例，键为String类型，值为Integer类型
        Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
        // 按顺序添加键值对
        linkedHashMap.put("first", 1);   // 第一个插入的元素
        linkedHashMap.put("second", 2);  // 第二个插入的元素
        linkedHashMap.put("third", 3);   // 第三个插入的元素
        
        System.out.println("LinkedHashMap保持插入顺序:");
        // 遍历LinkedHashMap，验证其保持插入顺序的特性
        for (Map.Entry<String, Integer> entry : linkedHashMap.entrySet()) {
            // entry.getKey()获取键，entry.getValue()获取值
            System.out.println(entry.getKey() + " => " + entry.getValue());
        }
        
        // 3. TreeMap测试 - 按键排序的Map实现
        // TreeMap基于红黑树实现，能够按照键的自然顺序或自定义比较器进行排序
        System.out.println("\n======= TreeMap测试 =======");
        // 创建TreeMap实例，键为String类型，值为Integer类型
        Map<String, Integer> treeMap = new TreeMap<>();
        // 添加无序的键值对，TreeMap会自动按键的自然顺序进行排序
        treeMap.put("banana", 2);  // 字典序靠后的键
        treeMap.put("apple", 5);   // 字典序靠前的键
        treeMap.put("orange", 3);  // 字典序中间的键
        treeMap.put("grape", 1);   // 字典序最靠前的键
        
        System.out.println("TreeMap按键自然排序:");
        // 遍历TreeMap，验证其按键排序的特性
        for (Map.Entry<String, Integer> entry : treeMap.entrySet()) {
            System.out.println(entry.getKey() + " => " + entry.getValue());
        }
        
        // 4. Map的多种遍历方式
        // 展示Map接口提供的不同遍历方法
        System.out.println("\n======= Map遍历方式 =======");
        // 创建测试用的HashMap
        Map<String, String> map = new HashMap<>();
        map.put("A", "Apple");    // A键对应Apple值
        map.put("B", "Banana");   // B键对应Banana值
        map.put("C", "Cat");      // C键对应Cat值
        map.put("D", "Dog");      // D键对应Dog值
        
        // 方式1: 使用entrySet遍历键值对 - 最常用且高效的遍历方式
        // entrySet()返回Map中所有映射关系的Set视图，每个元素是Map.Entry类型的键值对
        System.out.println("方式1 - entrySet遍历:");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            // entry.getKey()获取当前键值对的键
            // entry.getValue()获取当前键值对的值
            System.out.println(entry.getKey() + " : " + entry.getValue());
        }
        
        // 方式2: 使用keySet遍历键 - 先获取所有键，再通过键获取对应的值
        // keySet()返回Map中所有键的Set视图
        System.out.println("\n方式2 - keySet遍历:");
        for (String key : map.keySet()) {
            // 通过键获取对应的值
            System.out.println(key + " : " + map.get(key));
        }
        
        // 方式3: 使用values遍历值 - 只关心值而不关心键时使用
        // values()返回Map中所有值的Collection视图
        System.out.println("\n方式3 - values遍历:");
        for (String value : map.values()) {
            // 直接输出值，无法获取对应的键
            System.out.println(value);
        }
        
        // 方式4: 使用Iterator遍历 - 在遍历过程中需要删除元素时使用
        // 获取entrySet的迭代器，可以安全地在遍历过程中删除元素
        System.out.println("\n方式4 - Iterator遍历:");
        // 获取entrySet的迭代器对象
        Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
        // 判断是否还有下一个元素
        while (iterator.hasNext()) {
            // 获取下一个键值对
            Map.Entry<String, String> entry = iterator.next();
            System.out.println(entry.getKey() + " : " + entry.getValue());
        }
        
        // 5. 常用方法测试
        // 展示Map接口提供的一些实用方法
        System.out.println("\n======= 常用方法测试 =======");
        // 创建测试用的HashMap
        Map<String, Integer> testMap = new HashMap<>();
        testMap.put("score1", 85);  // 添加第一个成绩
        testMap.put("score2", 92);  // 添加第二个成绩
        
        // putIfAbsent方法 - 只有当键不存在时才添加键值对，避免覆盖已有的值
        // 如果指定的键已经存在，则不执行任何操作并返回已有的值
        testMap.putIfAbsent("score1", 100);  // score1已存在，不会覆盖，返回85
        // 如果指定的键不存在，则添加键值对并返回null
        testMap.putIfAbsent("score3", 78);   // score3不存在，会添加，返回null
        System.out.println("putIfAbsent后: " + testMap);
        
        // getOrDefault方法 - 获取指定键的值，如果键不存在则返回默认值
        // 获取存在的键对应的值
        System.out.println("score1的值: " + testMap.get("score1"));
        // 获取不存在的键时返回指定的默认值
        System.out.println("score4的值(默认0): " + testMap.getOrDefault("score4", 0));
        
        // replace方法 - 替换指定键的值
        // 只有当键存在时才会替换，并返回旧值
        testMap.replace("score2", 99);
        System.out.println("替换score2后: " + testMap);
        
        // 6. Map的函数式操作
        // Java 8引入的函数式编程特性，提供更简洁的操作方式
        System.out.println("\n======= Map函数式操作 =======");
        // 创建测试用的HashMap
        Map<String, Integer> funcMap = new HashMap<>();
        funcMap.put("math", 95);     // 数学成绩
        funcMap.put("english", 87);  // 英语成绩
        funcMap.put("chinese", 92);  // 语文成绩
        
        // forEach遍历 - 使用Lambda表达式遍历Map
        // 接受一个BiConsumer类型的参数，表示对每个键值对执行的操作
        System.out.println("forEach遍历:");
        funcMap.forEach((key, value) -> System.out.println(key + " : " + value));
        
        // replaceAll替换所有值 - 使用Lambda表达式对所有值进行转换
        // 接受一个BiFunction类型的参数，表示如何根据键和值生成新值
        System.out.println("\n所有成绩加5分:");
        funcMap.replaceAll((key, value) -> value + 5);
        System.out.println("加分后: " + funcMap);
        
        System.out.println("\nMap接口功能测试完成！");
    }
}